In [183]:
# Load all analyzers
import graph_analyzer
tickers = ['ANC', 'DGC', 'FTC', 'GLD', 'LKY', 'MNC', 'NMC', 'NVC', 'PPC', 'LTC', 'DRK', 'MEC']
analyzers = map(graph_analyzer.GraphAnalyzer, tickers)

Hypothesis 6: Analyzing Large Clusters

Richest Clusters


In [529]:
nmc_analyzer = graph_analyzer.GraphAnalyzer('NMC')

In [530]:
# Let's just print out the five richest clusters at present
richest_clusters = nmc_analyzer.richest_n_clusters(5)
for (cluster_id, balance) in richest_clusters:
    print 'Cluster %d: %d %s' % (cluster_id, balance, nmc_analyzer.ticker.upper())


Cluster 43651: 158259822469490 NMC
Cluster 1164683: 41489197141377 NMC
Cluster 954090: 13363029999585 NMC
Cluster 932016: 9000000000000 NMC
Cluster 431666: 7900000000000 NMC

In [531]:
# Now let's compute the ratio between adjacent balances
for i in range(len(richest_clusters) - 1):
    (cluster_id1, balance1) = richest_clusters[i]
    (cluster_id2, balance2) = richest_clusters[i + 1]
    ratio = balance1 / float(balance2)
    print 'Balance(Cluster %d)/Balance(Cluster %d): %f' % (cluster_id1, cluster_id2, ratio)


Balance(Cluster 43651)/Balance(Cluster 1164683): 3.814483
Balance(Cluster 1164683)/Balance(Cluster 954090): 3.104775
Balance(Cluster 954090)/Balance(Cluster 932016): 1.484781
Balance(Cluster 932016)/Balance(Cluster 431666): 1.139241

The richest cluster for Namecoin isthree times richer than the second richest, and over 11 times richer than the third richest.


In [34]:
# Now let's look at his mining activity. First, load all the miner IDs.
import mining
nmc_mining_analyzer = mining.MiningAnalyzer(nmc_analyzer)
pct_mined = nmc_mining_analyzer.pct_blocks_by_cluster(cluster_id)
print 'Cluster %d mined %.2f%% of blocks' % (cluster_id, pct_mined)


Cluster 359531 mined 8.48% of blocks

Wealth Concentration

How many individuals control $x$% of the wealth over time?


In [47]:
# Compute total wealth in separate cell
tx_ids = [1, 10, 100, 1000, 5000, 10000, 50000, 100000, 200000, 400000, 800000, 1600000]
total_wealth= [nmc_analyzer.balance_for_blockchain(tx_id=tx_id) for tx_id in tx_ids]

# Get 10,000 richest individuals -- this should always be enough
richest_clusters = [nmc_analyzer.richest_n_clusters(10000, tx_id=tx_id) for tx_id in tx_ids]

In [51]:
def num_entities_required(pct_wealth, tx_id):
    idx = tx_ids.index(tx_id)
    cutoff = pct_wealth * total_wealth[idx]
    total = 0
    for i, (_, balance) in enumerate(richest_clusters[idx]):
        total += balance
        if total >= cutoff:
            return (i + 1)

In [58]:
pcts = [0.25, 0.50, 0.75, 0.90, 0.99]
data = [map(lambda tx_id: num_entities_required(pct, tx_id), tx_ids) for pct in pcts]
for i, series in enumerate(data):
    plt.figure(i)
    plt.title('# Entities to Hold %.2f%% of Coins' % pcts[i])
    plt.ylabel('# Entities Required')
    plt.xlabel('# Transactions')
    plt.plot(tx_ids, series)


Now, what percentage of the wealth is controlled by the richest $n$ individuals?


In [65]:
def pct_held_by_top_n(n, tx_id):
    idx = tx_ids.index(tx_id)
    total = sum([balance for (_, balance) in richest_clusters[idx][:n]])
    return 100.0 * total / total_wealth[idx]

In [66]:
ns = [1, 5, 10, 100, 1000]
data = [map(lambda tx_id: pct_held_by_top_n(n, tx_id), tx_ids) for n in ns]
for i, series in enumerate(data):
    plt.figure(i)
    plt.title('%% of Coins Held by Top %d Entities' % ns[i])
    plt.ylabel('% of Coins')
    plt.xlabel('# Transactions')
    plt.plot(tx_ids, series)


Largest Clusters


In [18]:
# How about number of addresses?
largest_clusters = nmc_analyzer.largest_n_clusters(5)
for (cluster_id, size) in largest_clusters:
    print 'Cluster %d: %d addresses' % (cluster_id, size)


Cluster 214652: 38202 addresses
Cluster 227283: 33855 addresses
Cluster 117447: 16127 addresses
Cluster 374410: 6005 addresses
Cluster 372660: 5921 addresses

In [19]:
# Compute the ratio between adjacent sizes
for i in range(len(largest_clusters) - 1):
    (cluster_id1, size1) = largest_clusters[i]
    (cluster_id2, size2) = largest_clusters[i + 1]
    ratio = size1 / float(size2)
    print 'Size(Cluster %d)/Size(Cluster %d): %f' % (cluster_id1, cluster_id2, ratio)


Size(Cluster 214652)/Size(Cluster 227283): 1.128401
Size(Cluster 227283)/Size(Cluster 117447): 2.099275
Size(Cluster 117447)/Size(Cluster 374410): 2.685595
Size(Cluster 374410)/Size(Cluster 372660): 1.014187

The same outlier effect isn't present when you sort by cluster size.

Early Miners

Interesting and points to scams.


In [236]:
mining_analyzers = map(mining.MiningAnalyzer, analyzers)

In [336]:
tx_id = 10000
pct_change = []
for (azr, mining_azr) in zip(analyzers, mining_analyzers):
    # How many of the first 10,000 transactions were coin generation?
    miner_pubkeys = mining_azr.miner_pubkeys(tx_id=tx_id)
    print 'In %s, out of the first %d transactions, %d are coin generation' % (azr.ticker.upper(), tx_id, len(miner_pubkeys))
    
    # Next, group these early miners by cluster
    miner_clusters = set(azr.clusters_by_pubkey[pubkey_id] for pubkey_id in miner_pubkeys)
    pct = len(miner_pubkeys) / float(len(miner_clusters))
    print 'Of the first %d miners, there are %d distinct clusters (%.2f blocks per)' % (len(miner_pubkeys), len(miner_clusters), pct)
    
    # Next, compute the balances of these clusters
    total = sum(azr.balance_for_cluster(cluster_id, tx_id=tx_id) for cluster_id in miner_clusters)
    total_rank = None
    total_now = sum(azr.balance_for_cluster(cluster_id) for cluster_id in miner_clusters)
    total_now_rank = None
    for (i, (cluster_id, balance)) in enumerate(azr.richest_n_clusters(1000)):
        if balance < total and total_rank == None:
            total_rank = i + 1
        
        if balance < total_now and total_now_rank == None:
            total_now_rank = i + 1
        
        if total_rank != None and total_now_rank != None:
            break

    print 'At the time, they held %d %s' % (total, azr.ticker.upper())
    print 'Today, this would put them, as a group, %d on the %s richlist' % (total_rank, azr.ticker.upper())
    print 'Today, they hold %d %s' % (total_now, azr.ticker.upper())
    print 'This would put them, as a group, %d on the %s richlist' % (total_now_rank, azr.ticker.upper())
    pct_change.append(100.0 * (total - total_now) / total)
    if total_now > total:
        print 'Their wealth has increased by %.8f%%' % (100.0 * (total_now - total) / total)
    else:
        print 'Their wealth has dropped by %.8f%%' % (100.0 * (total - total_now) / total)
    
    print ''


In ANC, out of the first 10000 transactions, 9619 are coin generation
Of the first 9619 miners, there are 1542 distinct clusters (6.24 blocks per)
At the time, they held 3670445130922 ANC
Today, this would put them, as a group, 7 on the ANC richlist
Today, they hold 33400518555277 ANC
This would put them, as a group, 1 on the ANC richlist
Their wealth has increased by 809.98550214%

In DGC, out of the first 10000 transactions, 9401 are coin generation
Of the first 9401 miners, there are 1126 distinct clusters (8.35 blocks per)
At the time, they held 7474726248773 DGC
Today, this would put them, as a group, 28 on the DGC richlist
Today, they hold 67112928927334 DGC
This would put them, as a group, 6 on the DGC richlist
Their wealth has increased by 797.86470693%

In FTC, out of the first 10000 transactions, 9240 are coin generation
Of the first 9240 miners, there are 428 distinct clusters (21.59 blocks per)
At the time, they held 170036111383010 FTC
Today, this would put them, as a group, 5 on the FTC richlist
Today, they hold 616345838227227 FTC
This would put them, as a group, 3 on the FTC richlist
Their wealth has increased by 262.47937759%

In GLD, out of the first 10000 transactions, 9842 are coin generation
Of the first 9842 miners, there are 789 distinct clusters (12.47 blocks per)
At the time, they held 637832575004424 GLD
Today, this would put them, as a group, 2 on the GLD richlist
Today, they hold 326342855453633 GLD
This would put them, as a group, 2 on the GLD richlist
Their wealth has dropped by 48.83565559%

In LKY, out of the first 10000 transactions, 4862 are coin generation
Of the first 4862 miners, there are 438 distinct clusters (11.10 blocks per)
At the time, they held 29399304893697 LKY
Today, this would put them, as a group, 9 on the LKY richlist
Today, they hold 7170879043621 LKY
This would put them, as a group, 17 on the LKY richlist
Their wealth has dropped by 75.60867827%

In MNC, out of the first 10000 transactions, 9912 are coin generation
Of the first 9912 miners, there are 746 distinct clusters (13.29 blocks per)
At the time, they held 94673315000000 MNC
Today, this would put them, as a group, 1 on the MNC richlist
Today, they hold 2902831235249 MNC
This would put them, as a group, 18 on the MNC richlist
Their wealth has dropped by 96.93384431%

In NMC, out of the first 10000 transactions, 6989 are coin generation
Of the first 6989 miners, there are 3427 distinct clusters (2.04 blocks per)
At the time, they held 14218085115000 NMC
Today, this would put them, as a group, 3 on the NMC richlist
Today, they hold 47515287134773 NMC
This would put them, as a group, 2 on the NMC richlist
Their wealth has increased by 234.18907504%

In NVC, out of the first 10000 transactions, 7883 are coin generation
Of the first 7883 miners, there are 136 distinct clusters (57.96 blocks per)
At the time, they held 185850414031 NVC
Today, this would put them, as a group, 1 on the NVC richlist
Today, they hold 185392502072 NVC
This would put them, as a group, 1 on the NVC richlist
Their wealth has dropped by 0.24638738%

In PPC, out of the first 10000 transactions, 6886 are coin generation
Of the first 6886 miners, there are 129 distinct clusters (53.38 blocks per)
At the time, they held 6620029466693 PPC
Today, this would put them, as a group, 1 on the PPC richlist
Today, they hold 3032393509394 PPC
This would put them, as a group, 1 on the PPC richlist
Their wealth has dropped by 54.19365541%

In LTC, out of the first 10000 transactions, 9903 are coin generation
Of the first 9903 miners, there are 949 distinct clusters (10.44 blocks per)
At the time, they held 46049688100000 LTC
Today, this would put them, as a group, 8 on the LTC richlist
Today, they hold 293455989714714 LTC
This would put them, as a group, 3 on the LTC richlist
Their wealth has increased by 537.25945131%

In DRK, out of the first 10000 transactions, 4839 are coin generation
Of the first 4839 miners, there are 688 distinct clusters (7.03 blocks per)
At the time, they held 151061271566648 DRK
Today, this would put them, as a group, 1 on the DRK richlist
Today, they hold 85162317640731 DRK
This would put them, as a group, 1 on the DRK richlist
Their wealth has dropped by 43.62398995%

In MEC, out of the first 10000 transactions, 9994 are coin generation
Of the first 9994 miners, there are 7399 distinct clusters (1.35 blocks per)
At the time, they held 499250000000000 MEC
Today, this would put them, as a group, 2 on the MEC richlist
Today, they hold 365498417594197 MEC
This would put them, as a group, 2 on the MEC richlist
Their wealth has dropped by 26.79050223%


In [ ]:
# Plot the number of miners over time
tx_ids = [10, 100, 500, 1000, 5000, 10000, 25000, 50000, 75000, 100000, 200000, 300000, 400000]

def mining_stats(azr, mining_azr, tx_id):
    miner_keys = mining_azr.miner_pubkeys(tx_id=tx_id)
    num_blocks = len(miner_keys)
    distinct_miners = set(mining_azr.miner_pubkeys(tx_id=tx_id))
    distinct_entities = set(azr.clusters_by_pubkey[pk] for pk in distinct_miners)
    wealth_held = sum([azr.balance_for_cluster(cluster_id, tx_id=tx_id) for cluster_id in distinct_entities])
    total_wealth = azr.balance_for_blockchain(tx_id=tx_id)
    pct_held = wealth_held / float(total_wealth)
    return (num_blocks, len(distinct_miners), len(distinct_entities), pct_held)

data = []
for (azr, mining_azr) in zip(analyzers, mining_analyzers):
    tx_ids_adjusted = tx_ids + [azr.now_tx_id]
    series = map(lambda tx_id: mining_stats(azr, mining_azr, tx_id), tx_ids_adjusted)
    data.append(series)

In [282]:
for i, series in enumerate(data):
    azr = analyzers[i]
    mining_azr = mining_analyzers[i]
    xs = map(lambda tx_id: min(tx_id, azr.now_tx_id), tx_ids + [azr.now_tx_id])
    xs = map(lambda tx_id: len(mining_azr.miner_pubkeys(tx_id=tx_id)), xs)
    ys = zip(*series)[2]
    plt.figure(i)
    plt.title('%s: # Distinct Miners Over Time' % azr.ticker.upper())
    plt.ylabel('# Distinct Miners')
    plt.xlabel('Block #')
    plt.plot(xs, ys)
    
    ys = zip(*series)[3]
    plt.figure(len(data) + i)
    plt.title('%s: Pct. Coins Held by Miners Over Time' % azr.ticker.upper())
    plt.ylabel('Pct. of Coins')
    plt.xlabel('Block #')
    plt.plot(xs, ys)


Hypothesis 7: Deanonymizing Big Players

We can use public keys, associated with individuals around the Web, to try and deanonymize and extract patterns from the transaction graph.


In [193]:
def print_addr_stats(analyzer, name, address):
    (cluster_id, balance) = balance_for_addr(analyzer, addr)
    (rank, num_clusters) = ranking_for_cluster(analyzer, cluster_id)
    print '%s owns %d %s' % (name, balance, analyzer.ticker.upper())
    print 'Ranks %d out of %d' % (rank, num_clusters)

Namecoin

explorer.namecoin.info lists address N1KHAL5C1CRzy58NdJwp1tbLze3XrkFxx9 for donations. Coincidentally, this same address is listed as belonging to 'Khal' on bitcointalk.org.

Khal is the lead developer on the Namecoin project.


In [383]:
from graph_analyzer import GraphAnalyzer
nmc_analyzer = GraphAnalyzer('NMC')

In [533]:
khal_pubkey = 'N1KHAL5C1CRzy58NdJwp1tbLze3XrkFxx9'
khal_pubkey_id = nmc_analyzer.pubkey_for_address(khal_pubkey)
khal_cluster_id = nmc_analyzer.cluster_for_pubkey(khal_pubkey_id)
khal_balance = nmc_analyzer.balance_for_cluster(khal_cluster_id)
print 'Khal\'s balance: %d %s' % (khal_balance, nmc_analyzer.ticker)


Khal's balance: 13262523144 nmc

In [535]:
# Where does this rank?
richest_clusters = nmc_analyzer.richest_n_clusters(10000)
richest_cluster_id = [cluster_id for (cluster_id, _) in richest_clusters]
khal_idx = richest_cluster_id.index(khal_cluster_id)
num_clusters = len(nmc_analyzer.clusters)
print 'Khal\'s cluster ranks %d on the wealth list (out of %d)' % (khal_idx, num_clusters)


Khal's cluster ranks 1329 on the wealth list (out of 1490408)

Anoncoin

Like Khal, Meeh is the lead developer for Anoncoin. He's also the creator.

On his BitcoinTalk profile, he lists public key AMeehr1AtpyMaZKVLv4Tb1wdRtVfx3wDj3.


In [536]:
from graph_analyzer import GraphAnalyzer
anc_analyzer = GraphAnalyzer('ANC')

In [537]:
meeh_pubkey = 'AMeehr1AtpyMaZKVLv4Tb1wdRtVfx3wDj3'
meeh_pubkey_id = anc_analyzer.pubkey_for_address(meeh_pubkey)
meeh_cluster_id = anc_analyzer.cluster_for_pubkey(meeh_pubkey_id)
meeh_balance = anc_analyzer.balance_for_cluster(meeh_cluster_id)
print 'Meeh\'s balance: %d %s' % (meeh_balance / discount, anc_analyzer.ticker.upper())


Meeh's balance: 327678 ANC

In [538]:
# Where does this rank?
richest_clusters = anc_analyzer.richest_n_clusters(100)
richest_cluster_id = [id for (id, _) in richest_clusters]
meeh_idx = richest_cluster_id.index(meeh_cluster_id)
num_clusters = len(anc_analyzer.clusters)
print 'Meeh\'s cluster ranks %d on the wealth list (out of %d)' % (meeh_idx + 1, num_clusters)


Meeh's cluster ranks 1 on the wealth list (out of 16770)

Novacoin

The Novacoin bitcointalk thread lists active mining pools. One such pool, VIRPOOL, lists an address to which users can send donations: 4YMXC27BAfRTqHaWTRNi2aCAT84b83YFsC.

We can track the addresses linked to this address to check the balance of the pool and its ranking within the network.


In [65]:
from graph_analyzer import GraphAnalyzer
nvc_analyzer = GraphAnalyzer('NVC')

In [98]:
def balance_for_addr(analyzer, addr):
    pubkey_id = analyzer.pubkey_for_address(addr)
    cluster_id = analyzer.cluster_for_pubkey(pubkey_id)
    balance = analyzer.balance_for_cluster(cluster_id)
    return (cluster_id, balance)

In [351]:
virpool_pubkey = '4YMXC27BAfRTqHaWTRNi2aCAT84b83YFsC'
(cluster_id, balance) = balance_for_addr(nvc_analyzer, virpool_pubkey)
print 'VIRPOOL (cluster %d) owns %d %s' % (cluster_id, balance, nvc_analyzer.ticker.upper())


VIRPOOL (cluster 2131) owns 171447786525 NVC

In [405]:
def ranking_for_cluster(analyzer, cluster_id):
    richest_clusters = analyzer.richest_n_clusters(100000)
    richest_cluster_id = [id for (id, _) in richest_clusters]
    idx = richest_cluster_id.index(cluster_id)
    num_clusters = len(analyzer.clusters)
    return (idx + 1, num_clusters)

In [113]:
(rank, num_clusters) = ranking_for_cluster(nvc_analyzer, cluster_id)
print 'VIRPOOL ranks %d on the wealth list (out of %d entities)' % (rank, num_clusters)


VIRPOOL ranks 1 on the wealth list (out of 19127 entities)

The leading Novacoin P2P mining pool, novaco.in, also lists an address 4ZLoY6T8wXKgrbZM9gL8fQt48NUzGo2yAg. It seems that this address is part of the same entity as VIRPOOL. This is further evidenced by this transaction, where an address on the left contains Vir and the address on the right is the novaco.in address.


In [349]:
novacoin_pubkey = '4ZLoY6T8wXKgrbZM9gL8fQt48NUzGo2yAg'
(cluster_id, balance) = balance_for_addr(nvc_analyzer, novacoin_pubkey)
print 'Novaco.in is in cluster %d' % cluster_id


Novaco.in is in cluster 2131

Based on this thread, someone named svost (who appears to be a core developer) may have control of this cluster.


In [350]:
svost_pubkey = '4SvostvrMWxU7hYR1aDij22AysKSG4QKeu '
(cluster_id, balance) = balance_for_addr(nvc_analyzer, svost_pubkey)
print 'svost is in cluster %d' % cluster_id


svost is in cluster 2131

Finally, the creator of Novacoin, Balthazar, lists this address on his bitcointalk profile: 4RgnHWtnJWEyMhqhDdazW3Hdr7cx5ybF6i.


In [120]:
balthazar_pubkey = '4RgnHWtnJWEyMhqhDdazW3Hdr7cx5ybF6i'
(cluster_id, balance) = balance_for_addr(nvc_analyzer, balthazar_pubkey)
(rank, num_clusters) = ranking_for_cluster(nvc_analyzer, cluster_id)
print 'Balthazar (cluster %d) owns %d %s' % (cluster_id, balance, nvc_analyzer.ticker.upper())
print 'Balthazar ranks %d on the wealth list (out of %d entities)' % (rank, num_clusters)


Balthazar (cluster 2549) owns 4000000 NVC
Balthazar ranks 1971 on the wealth list (out of 19127 entities)

Peercoin


In [138]:
from graph_analyzer import GraphAnalyzer
ppc_analyzer = GraphAnalyzer('PPC')

Best I could find is Sentinelrv, a member of the Peercoin team working on community.


In [155]:
sentinelrv_addr = 'PNH9rirzGKi6xAYKrrhAvnRE1pnJEQRPs8'
(rank, num_clusters) = ranking_for_cluster(nvc_analyzer, balance_for_addr(ppc_analyzer, sentinelrv_addr)[0])
print 'Sentinelrv ranks %d on the wealth list (out of %d entities)' % (rank, num_clusters)


Sentinelrv ranks 10988 on the wealth list (out of 19127 entities)

Digitalcoin


In [157]:
from graph_analyzer import GraphAnalyzer
dgc_analyzer = GraphAnalyzer('DGC')

In [159]:
foundation_addr = 'DEhowBduoYuw2pPjQJ81XAPDx18jQbG1nj'
ranking_for_cluster(nvc_analyzer, balance_for_addr(dgc_analyzer, foundation_addr)[0])


Out[159]:
(3041, 19127)

In [169]:
xawksow_addr = 'D8KU86c7sf8L9UPLU3jMLeadgaNsWrYMov'
ranking_for_cluster(nvc_analyzer, balance_for_addr(dgc_analyzer, xawksow_addr)[0])


Out[169]:
(6748, 19127)

Megacoin

A particularly tight-knit community with a lot of public addresses.


In [170]:
from graph_analyzer import GraphAnalyzer
mec_analyzer = GraphAnalyzer('MEC')

In [175]:
ethought_addr = 'MD63SLhdeV9DQfcQpQnP4eisDjVYL2H55G'
ranking_for_cluster(mec_analyzer, balance_for_addr(mec_analyzer, ethought_addr)[0])


Out[175]:
(2, 36751)

In [177]:
addr = 'MQVWq6pMkbACS7aMEjnYgggFr3Ziqr2mv5'
ranking_for_cluster(mec_analyzer, balance_for_addr(mec_analyzer, addr)[0])


Out[177]:
(35, 36751)

In [178]:
addr = 'MW11rq3aFtKE3kRACpd2TPmziu6WizPGYU'
ranking_for_cluster(mec_analyzer, balance_for_addr(mec_analyzer, addr)[0])


Out[178]:
(2, 36751)

In [179]:
addr = 'MGk8Jcf7wss4qUKfEgmeCMwnQjCHsFYQgp'
ranking_for_cluster(mec_analyzer, balance_for_addr(mec_analyzer, addr)[0])


Out[179]:
(33, 36751)

In [180]:
addr = 'MAK5Kod12QF8FY46LiQa7YUpYkvoC5WLLb'
ranking_for_cluster(mec_analyzer, balance_for_addr(mec_analyzer, addr)[0])


Out[180]:
(143, 36751)

nbk


In [181]:
addr = 'MDnML1znxhbfrSX5nU2GqP5FjbB4Gwpqsj'
ranking_for_cluster(mec_analyzer, balance_for_addr(mec_analyzer, addr)[0])


Out[181]:
(8833, 36751)

According to the forum post, VR4Mines donated 5000 MEC to the developer fund. By looking at a block explorer, we can find the address for VR4Mines: MQNaAeu1LWFmBZLhAtzst3svyztKUBNwdV.


In [182]:
addr = 'MQNaAeu1LWFmBZLhAtzst3svyztKUBNwdV'
ranking_for_cluster(mec_analyzer, balance_for_addr(mec_analyzer, addr)[0])


Out[182]:
(24, 36751)

Notice that ethought and Janek, big community members, appear to be in the same cluster.

After a lot of manual labor, I was able to trace the path by which they became connected:

  • At tx 386348, 301581 and 294223 (Janek) appear as inputs, implying that they belong to the same entity.
  • At tx 464607, 373307 and 294655 (ethought) appear as inputs, implying that they belong to the same entity.
  • Then, at tx 494891, 373307 and 301581 appear as inputs.

Litecoin


In [ ]:
from graph_analyzer import GraphAnalyzer
ltc_analyzer = GraphAnalyzer('LTC')

Charlie Lee, also known as coblee, is the creator of Litecoin. He claims on Reddit to own a modest amount of Litecoin. We can assess that claim by analyzing the addresses associated with the first few blocks (i.e., those blocks that only Charlie would be able to mine).


In [208]:
coblee_addrs = [
    'LeL16MDWU5jS3oeHHwEZRBTDExwPbNgcvy',
    'LSdTvMHRm8sScqwCi6x9wzYQae8JeZhx6y',
    'LYAj2j9pzSCNPBg9A4dbWUgmMiQDfrrY5N',
    'LKiiNMCKiGxfo1M5zqmHZQb5X1j3i69ZHX',
    'LadVH7kj2BYB68rVtPfCtjkstkrBPX8mBc'
]

In [539]:
for addr in coblee_addrs:
    print_addr_stats(ltc_analyzer, 'Charlie Lee', addr)


Charlie Lee owns 6400170434 LTC
Ranks 20853 out of 638630
Charlie Lee owns 6400170434 LTC
Ranks 20853 out of 638630
Charlie Lee owns 6400170434 LTC
Ranks 20853 out of 638630
Charlie Lee owns 6400170434 LTC
Ranks 20853 out of 638630
Charlie Lee owns 6400170434 LTC
Ranks 20853 out of 638630

Notice that they all link to the same entity, and that the sum is indeed modest.

We can also find other public keys online that let us do additional analysis.


In [406]:
addrs_by_name = [
    ('Litecoin Core', 'LcUP7ZU3Xpk1BUR3qut3dTjC3aK5JoZMYx'),
    ('Syari', 'LgRdmdHNRaW2KeUttBfW5NHvaCfj2zSSRg'),
    ('Ozcoin Donation Link', 'LScBB865pq249wBVF21Y3yr4QVZZ95SRFV'),
    ('Ozcoin Coinbase', 'LdCE2MxB7sAXvewsRDUmpR1UDxc8jVR7MF'),
    ('Coinotron', 'LPkxsW2LWmbUj9UMY9W6ocwEzbEkJhpvEc'),
    ('litecoinpool', 'LTCPooLqTK1SANSNeTR63GbGwabTKEkuS7'),
    ('Discus Fish', 'LajyQBeZaBA1NkZDeY8YT5RYYVRkXMvb2T'),
    ('explorer.litecoin.net', 'LYmpJZm1WrP5FSnxwkV2TTo5SkAF4Eha31')
]
for (name, addr) in addrs_by_name:
    try:
        print_addr_stats(ltc_analyzer, name, addr)
    except:
        # If it throws here, the address's balance is too low to be of interest
        pass


Litecoin Core owns 191932671848 LTC
Ranks 1353 out of 638630
Ozcoin Donation Link owns 278997453379828 LTC
Ranks 3 out of 638630
litecoinpool owns 3354096052170 LTC
Ranks 62 out of 638630
Discus Fish owns 278997453379828 LTC
Ranks 3 out of 638630
explorer.litecoin.net owns 305128627 LTC
Ranks 85310 out of 638630

Mincoin: A Scam? (Premine Analysis)

Mincoin started off with 500 MNC block rewards, which is a 250000% increase from the current block reward of 2 MNC. Some have labeled this a Scamcoin, as the early miners were able to take a quick stake in the coinage.

(An actual quote from their bitcointalk release: "With MinCoin only having 10 millions total coins to ever be produced compared to 21 millions for Bitcoin, it is pretty easy to see which one is more rare.")

Specifically, the first 1439 blocks gave a 500 MNC reward (see 1439 vs. 1440). This means that over 7% of the currency was mined in the first 1439 blocks.


In [540]:
# Get correct (azr, mining_azr) pair
for (azr, mining_azr) in zip(analyzers, mining_analyzers):
    if azr.ticker.upper() == 'MNC':
        break

In [329]:
# Note that block reward on block 1439 maps to transaction with ID 1440
tx_id = 1439

# How many of the first 10,000 transactions were coin generation?
miner_pubkeys = mining_azr.miner_pubkeys(tx_id=tx_id)
print 'In %s, out of the first %d transactions, %d are coin generation' % (azr.ticker.upper(), tx_id, len(miner_pubkeys))

# Next, group these early miners by cluster
miner_clusters = set(azr.clusters_by_pubkey[pubkey_id] for pubkey_id in miner_pubkeys)
pct = len(miner_pubkeys) / float(len(miner_clusters))
print 'Of the first %d miners, there are %d distinct clusters (%.2f blocks per)' % (len(miner_pubkeys), len(miner_clusters), pct)
print ''

# Next, compute the balances of these clusters at tx_id
total = sum(azr.balance_for_cluster(cluster_id, tx_id=tx_id) for cluster_id in miner_clusters)
for (i, (cluster_id, balance)) in enumerate(azr.richest_n_clusters(1000)):
    if balance < total:
        print 'After the first 1440 transactions, these miners controlled %d %s' % (total, azr.ticker.upper())
        print 'Compared to today\'s richest, this would put them, as a group, %d on the %s richlist' % (i + 1, azr.ticker.upper())
        print ''
        break

# Finally, compare this to the balances of these clusters today
total_now = sum(azr.balance_for_cluster(cluster_id) for cluster_id in miner_clusters)
for (i, (cluster_id, balance)) in enumerate(azr.richest_n_clusters(1000)):
    if balance < total_now:
        print 'Today, these miners control %d %s' % (total_now, azr.ticker.upper())
        print 'This puts them, as a group, %d on the %s richlist' % (i + 1, azr.ticker.upper())
        print ''
        break

diff = total - total_now
pct = diff / float(total)
print 'These miners have cashed out an estimated %d %s, or %.5f of their original holdings' % (diff, azr.ticker.upper(), pct)


In MNC, out of the first 1439 transactions, 1438 are coin generation
Of the first 1438 miners, there are 26 distinct clusters (55.31 blocks per)

After the first 1440 transactions, these miners controlled 71905000000000 MNC
Compared to today's richest, this would put them, as a group, 1 on the MNC richlist

Today, these miners control 11410272605 MNC
This puts them, as a group, 284 on the MNC richlist

These miners have cashed out an estimated 71893589727395 MNC, or 0.99984 of their original holdings

You can get similar (but slightly less extreme) numbers by looking at Goldcoin up to transaction 371. Till then, the block reward was 1000000000000. The miners would, in today's world, have been fourth richest, and had cashed out over 99% of their holdings.

This analysis is useful, but it's possible that coins were simply moved to other addresses held by the same entity (i.e., that our clustering was too conservative to track movement). This alone is not evidence of a "pump and dump".


In [541]:
# People say that Mincoin is a scam because the first 4,319 blocks had huge rewards (500, then 200, then 50, and then 2).
block_idx = 4319
tx_id = mining_azr.tx_ids[block_idx]
pubkeys = mining_azr.miner_pubkeys(tx_id=tx_id)
miners = set(pubkeys)
clusters = set(azr.clusters_by_pubkey[pubkey] for pubkey in miners)
print 'In this early stage, there are %d unique miner public keys, but %d clusters' % (len(miners), len(clusters))


In this early stage, there are 4319 unique miner public keys, but 223 clusters

In [544]:
# The address MBodnedZArLoJSZ4ganaNGcccaPFbbk4Ne was found on a forum to be associated with Cryptsy, an exchange
pubkey = azr.pubkey_for_address('MBodnedZArLoJSZ4ganaNGcccaPFbbk4Ne')
cluster_id = azr.clusters_by_pubkey[pubkey]
for i, (cluster, balance) in enumerate(azr.richest_n_clusters(10)):
    if cluster == cluster_id:
        break
print 'Cryptsy holds %d %s, ranking it %d on the rich list' % (balance, azr.ticker.upper(), i + 1)


Cryptsy holds 60216839659839 MNC, ranking it 1 on the rich list

In [496]:
# Let's plot its balance over time
xs = [1, 10, 100, 1000, 10000, 20000, 30000, 50000, 75000, 85000, 100000, 150000, 200000, 250000, 300000, 320000, 330000, 340000, 350000, 400000, 450000, 500000, 550000, 600000, 650000, 700000, azr.now_tx_id]
ys = []
for tx_id in xs:
    ys.append(azr.balance_for_cluster(3027, tx_id=tx_id))

plt.plot(xs, ys)


Out[496]:
[<matplotlib.lines.Line2D at 0x350113110>]

In [549]:
# What causes the spike around transaction 300,000?
# In the blockchain, we find that a huge deposit is made from address MMCSrkbKCDgxZtw7v7cd4uhxtvYPkfNBhF.
pubkey = azr.pubkey_for_address('MMCSrkbKCDgxZtw7v7cd4uhxtvYPkfNBhF')
cluster_id = azr.clusters_by_pubkey[pubkey]
print 'Around the time of the transaction, this pubkey holds %d %s' % (azr.balance_for_cluster(cluster_id, tx_id=320000), azr.ticker.upper())
print 'It maps to cluster %d' % cluster_id


Around the time of the transaction, this pubkey holds 73273441073628 MNC
It maps to cluster 1784

In [550]:
pubkey = azr.pubkey_for_address('MNRRNX3a7U1hFjVK9xhmDCNH1K4F5fLpTa')
cluster_id = azr.clusters_by_pubkey[pubkey]
print 'An address known to SuperTramp also maps to cluster %d' % cluster_id


An address known to SuperTramp also maps to cluster 1784

In [554]:
# Smoking gun: http://mnc.cryptoexplore.com/tx/d74723fbfde248bbc82ad5579a535b7b8f8f8625f9768ba2ef7107b62cef03fe
# Maps to tx_id 308030
tx_id = 308030

supertramp_address = 'MNRRNX3a7U1hFjVK9xhmDCNH1K4F5fLpTa' # from forum
cryptsy_address = 'MBodnedZArLoJSZ4ganaNGcccaPFbbk4Ne' # output of DaveM's forum post

cryptsy_depositor_input_address = 'MMCSrkbKCDgxZtw7v7cd4uhxtvYPkfNBhF' # from tx_id 308030
cryptsy_depositor_output_address = 'MCUVuMmsVXDJAdNt7qJH9hXe82XDc2hyJW' # from tx_id 308030

if azr.clusters_by_pubkey[azr.pubkey_for_address(cryptsy_address)] == azr.clusters_by_pubkey[azr.pubkey_for_address(cryptsy_depositor_output_address)]:
    print 'Crypty\'s pubkey (from the forum post) and the output of the large transaction map to the same cluster'

if azr.clusters_by_pubkey[azr.pubkey_for_address(supertramp_address)] == azr.clusters_by_pubkey[azr.pubkey_for_address(cryptsy_depositor_input_address)]:
    print 'SuperTramp\'s pubkey and the input to the large transaction map to the same cluster'

print 'Therefore, it appears as if SuperTramp is exchanging over 50,000 MNC on Cryptsy in this transaction'


Crypty's pubkey (from the forum post) and the output of the large transaction map to the same cluster
SuperTramp's pubkey and the input to the large transaction map to the same cluster
Therefore, it appears as if SuperTramp is exchanging over 50,000 MNC on Cryptsy in this transaction

In [ ]: